# -*-coding:utf-8-*-
# -*- coding: utf-8 -*-
# @Time    : 2022/10/17 9:41
# @Author  : gaojy
# @Site    : 
# @File    : send_request.py
# @Software: PyCharm
import copy
import importlib
import json
import re
import traceback
from typing import Any

import jsonpath
import requests

from common.common_function import read_extract_yaml, get_parent_path, get_sys_code, write_extract_yaml
from common.log import MyLog, error_log

SYS_CODE = get_sys_code()
# 动态导入包
module = importlib.import_module(f"debugtalk.{SYS_CODE}")
LOG_PATH = get_parent_path() + "/log/log.txt"
log = MyLog("api", file = LOG_PATH)


class Request:

    def __init__(self):
        self.__base_url = "https://uaa-test.cdcdn.cn:22666"
        # 字符串中提取占位符"{{xx}}" --- 从接口提取的参数的占位符
        self.__extract_old_str = r"\{\{\w+\}\}"
        # 占位符中的变量“{{xx}}里的xx”
        self.__extract_new_str = r"\{\{(\w+)\}\}"
        # 占位符"${func(xx)}"
        self.__func_expr = r"\$\{\S+\}"
        # 占位符"${func(xx)}"中的“func(xx)”函数
        self.__func_info = r"\$\{(\S+)\}"
        #     占位符"${func(xx)}"里的"xx"
        self.__func_params = r"\((\S+)\)"

    # 替换{{}}形式的,接口响应的都是字符串形式
    def replace_extract_param(self, data: Any):
        if isinstance(data, str):
            for old_str, new_str in self.find_extract_expr(data).items():
                # 字符串直接替换
                data = data.replace(old_str, new_str)
            return data
        elif isinstance(data, list):
            for l_index, l_value in enumerate(data):
                if isinstance(l_value, str):
                    if "{{" in l_value:
                        for l_value_old, l_value_new in self.find_extract_expr(l_value).items():
                            if l_value.count("{{") == 1 and l_value.endswith("}}"):
                                l_value = l_value_new
                            else:
                                l_value = l_value.replace(l_value_old, l_value_new)
                        data[l_index] = l_value
                elif isinstance(l_value, list):
                    data[l_index] = self.replace_extract_param(l_value)
                elif isinstance(l_value, dict):
                    data[l_index] = self.replace_extract_param(l_value)
            return data
        elif isinstance(data, dict):
            for old_key, old_value in data.items():
                temp_value = copy.deepcopy(old_key)
                # key值占位符判断
                if isinstance(old_key, str):
                    if "{{" in old_key:
                        for k_old, k_new in self.find_extract_expr(old_key).items():
                            # 新增替换后的key与删除的key组成新的键值对
                            data[k_new] = data.pop(temp_value)
                            old_key = k_new
                # value占位符判断
                if isinstance(old_value, str):
                    if "{{" in old_value:
                        for o, n in self.find_extract_expr(old_value).items():
                            # 当字符串仅包含一对占位符时，采取直接赋值方法
                            if old_value.startswith("{{") and old_value.endswith("}}") and old_value.count("{{") == 1:
                                old_value = n
                            else:
                                old_value = old_value.replace(o, n)
                            data[old_key] = old_value

                elif isinstance(old_value, list):
                    data[old_key] = self.replace_extract_param(old_value)
                elif isinstance(old_value, dict):
                    data[old_key] = self.replace_extract_param(old_value)
            return data

        # 替换{{}}形式的,接口响应的都是字符串形式

    def replace_function_param(self, data: Any):
        if isinstance(data, str):
            for old_str, new_str in self.find_function_expr(data).items():
                # 字符串直接替换
                data = data.replace(old_str, new_str)
            return data
        elif isinstance(data, list):
            for l_index, l_value in enumerate(data):
                if isinstance(l_value, str):
                    if "${" in l_value:
                        for l_value_old, l_value_new in self.find_function_expr(l_value).items():
                            if l_value.count("${") == 1 and l_value.endswith("}"):
                                l_value = l_value_new
                            else:
                                l_value = l_value.replace(l_value_old, l_value_new)
                        data[l_index] = l_value
                elif isinstance(l_value, list):
                    data[l_index] = self.replace_function_param(l_value)
                elif isinstance(l_value, dict):
                    data[l_index] = self.replace_function_param(l_value)
            return data
        elif isinstance(data, dict):
            for old_key, old_value in data.items():
                temp_value = copy.deepcopy(old_key)
                # key值占位符判断
                if isinstance(old_key, str):
                    if "${" in old_key:
                        for k_old, k_new in self.find_function_expr(old_key).items():
                            # 新增替换后的key与删除的key组成新的键值对
                            data[k_new] = data.pop(temp_value)
                            old_key = k_new
                # value占位符判断
                if isinstance(old_value, str):
                    if "${" in old_value:
                        for o, n in self.find_function_expr(old_value).items():
                            # 当字符串仅包含一对占位符时，采取直接赋值方法
                            if old_value.startswith("${") and old_value.endswith("}") and old_value.count("${") == 1:
                                old_value = n
                            else:
                                old_value = old_value.replace(o, n)
                            data[old_key] = old_value

                elif isinstance(old_value, list):
                    data[old_key] = self.replace_function_param(old_value)
                elif isinstance(old_value, dict):
                    data[old_key] = self.replace_function_param(old_value)
            return data

    # 查找目标字符串全部{{var_name}}占位符并与占位符实际的值组成字典列表返回
    def find_extract_expr(self, data) -> dict:
        old_value = re.findall(self.__extract_old_str, data)
        new_value_list = re.findall(self.__extract_new_str, data)
        new_value = [read_extract_yaml(data) for data in new_value_list]
        return dict(zip(old_value, new_value))

    # 查找目标字符串全部${func(xx)}占位符并与占位符实际的值组成字典列表返回
    def find_function_expr(self, data) -> dict:
        old_value = re.findall(self.__func_expr, data)
        new_value = [self.hand_function(data) for data in old_value]
        return dict(zip(old_value, new_value))

    #  將${xx}的标识符替换成debugtalk真实值
    def hand_function(self, old_str: str) -> str:
        """
        :param old_str: 带${}的参数
        :return:
        """
        new_str = old_str
        f = re.findall(self.__func_info, old_str)[0]
        f_params_list = []
        f_params_dict = {}
        f_name = re.findall(r"\w+", f)[0]
        # 参数有值的情况下分离参数
        if re.findall(self.__func_params, f):
            f_params = re.findall(self.__func_params, f)[0]
            if f_params:
                if "," in f_params:
                    temp_list = f_params.split(",")
                    for data in temp_list:
                        if "=" in data:
                            temp_list_dict = data.split("=")
                            f_params_dict[temp_list_dict[0]] = temp_list_dict[1]
                        else:
                            f_params_list.append(data)

                elif "=" in f_params:
                    temp_list_dict = old_str.split("=")
                    f_params_dict[temp_list_dict[0]] = temp_list_dict[1]
                else:
                    f_params_list.append(f_params)
        #  调用函数
        if hasattr(module.DebugTalk(), f_name):
            if f_params_dict and f_params_list:
                new_str = getattr(module.DebugTalk(), f_name)(*f_params_list)(**f_params_dict)
            elif (not f_params_list) and f_params_dict:
                new_str = getattr(module.DebugTalk(), f_name)(**f_params_dict)
            elif (not f_params_dict) and f_params_list:
                new_str = getattr(module.DebugTalk(), f_name)(*f_params_list)
            elif (not f_params_dict) and (not f_params_list):
                new_str = getattr(module.DebugTalk(), f_name)()
        return new_str

    # 提取参数并存放至extract.yaml文件里,result是返回json形式，data是extract的值
    @staticmethod
    def save_extract_yaml(result: json, extract_data):
        for extract_method, extract_info in extract_data.items():
            for extract_key, extract_param in extract_info.items():
                extract_value = ""
                if extract_method == "jsonpath":
                    if jsonpath.jsonpath(result, f"$..{extract_param}"):
                        extract_value = jsonpath.jsonpath(result, f"$..{extract_param}")[0]
                elif extract_method == "regular":
                    if re.findall(r"{}".format(extract_param), result):
                        extract_value = re.findall(r"{}".format(extract_param), result)[0]
                # 写入中间文件
                write_extract_yaml({extract_key: extract_value})

    # 发送请求
    def send_request(self, url, method, headers, validate, extract, data = None, files = None, name = None):
        url = self.__base_url + url
        # 存在占位符的几个地方url body validate headers
        if "{{" in url:
            url = self.replace_extract_param(url)
        if "${" in url:
            url = self.replace_function_param(url)
        # headers替换
        headers_temp = json.dumps(headers)
        if "{{" in headers_temp:
            headers = self.replace_extract_param(headers)
        if "${" in headers_temp:
            headers = self.replace_function_param(headers)

        if data:
            data_temp = json.dumps(data)
            if "{{" in data_temp:
                data = self.replace_extract_param(data)
            if "${" in data_temp:
                data = self.replace_function_param(data)
        if method == "get":
            resp = requests.request(url = url, headers = headers, method = method, params = data)
        else:
            # 要用data传参json类型的参数需要将字典类型编码码成object
            if "Content-Type" in headers:
                if headers["Content-Type"] == "application/json":
                    data = json.dumps(data)
            log.info(f"接口名称:{name}")
            log.info(f"url:{url}")
            log.info(f"方法:{method}")
            log.info(f"请求头:{headers}")
            log.info(f"data:{data}")
            log.info(f"files:{files}")
            resp = requests.request(url = url, headers = headers, method = method, data = data, files = files)
            log.info(f"请求结束{'*' * 100}")

            # 响应类型
            resp_type = resp.headers["content-type"]
            if resp_type == "application/json":
                log.info(f"响应结果:{resp.json()}")
                # 断言
                if validate:
                    self.validate(resp.json(), validate)
                if extract:
                    self.save_extract_yaml(resp.json(), extract)
            elif resp_type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8":
                # 二进制文件生成文件名： 用例名+响应头的filename的后缀
                last_name = f".{resp.headers['filename'].split('.')[1]}" if headers[
                                                                                'filename'] in resp.headers else ".zip"
                file_name = name + last_name
                # 把二进制文件存入根目录file下
                with open(get_parent_path() + "file", "wb") as f:

                    f.write(resp.content)
            else:
                print("有待增加···········")

        return resp

    # 断言
    @staticmethod
    def validate(result, data):
        fail_mess = []
        try:
            for d in data:
                actual_result = ""
                # 查找实际结果
                if d["type"] == "jsonpath":
                    # 提取expr实际结果
                    actual_result = jsonpath.jsonpath(result, f"$..{d['expr']}")[0]
                elif d["type"] == "regular":
                    actual_result = re.findall(f"{d['expr']}", result)[0]
                elif d["type"] == "mysql":
                    actual_result = d['expr']
                #  判断比对方式
                #     相等断言
                if d['lable'] == "=":
                    if d["resp"] != actual_result:
                        fail_mess.append(f"实际结果{actual_result}!=预期结果{d['resp']}")
                    else:
                        log.info(f"{d['resp']} == {actual_result}")
                #   不等断言
                elif d["lable"] == "!=":
                    if d["resp"] != actual_result:
                        log.info(f"d['resp'] != actual_result")
                    else:
                        fail_mess.append(f"判断条件不等于不满足：实际结果{actual_result},预期结果{d['resp']}")

                #   包含断言
                elif d["lable"] == "contains":
                    if d["resp"] in actual_result:
                        continue
                    else:
                        fail_mess.append(f"判断条件包含不满足：实际结果{actual_result},预期结果{d['resp']}")
                #   不包含断言
                elif d["lable"] == "not_contains":
                    if d["resp"] not in actual_result:
                        continue
                    else:
                        fail_mess.append(f"判断条件不包含不满足：实际结果{actual_result},预期结果{d['resp']}")
                else:
                    # t=t+1
                    fail_mess.append(f"暂未有断言")
            if fail_mess:
                raise Exception(fail_mess)
        except Exception:
            error_log(f"断言出错，异常信息：{(traceback.format_exc())}")

    # 组装
    def analysis_parameters(self, case_dict):
        # 获取url
        url = case_dict["request"]["url"]
        # 获取方法
        method = case_dict["request"]["method"]
        # 获取headers
        headers = case_dict["request"]["headers"]
        # 如果有data 则获取，没有赋值为None
        data = None
        if "data" in case_dict["request"]:
            data = case_dict["request"]["data"]
        files_dict = None
        if "files" in case_dict["request"]:
            files_dict = {}
            for k, v in case_dict["request"]["files"].items():
                v = open(get_parent_path() + v, "rb")
                files_dict[k] = v

        # 断言替换
        if "validate" in case_dict:
            temp = json.dumps(case_dict["validate"])
            if "{{" in temp:
                if "{{" in temp:
                    data = self.replace_extract_param(data)
                if "${" in temp:
                    data = self.replace_function_param(data)
        # 发送请求
        self.send_request(url, method, headers, data = data, files = files_dict, name = case_dict['name'],
                          validate = case_dict["validate"], extract = case_dict["extract"])


if __name__ == "__main__":
    pass
